////////////////////////////////////////////////////////////////////////////////
//File:					UglyObjModel.cpp
//Author:				Kesitem
//This file is part of UglyProject.
//See UglyProjet.txt for copyright informations.
////////////////////////////////////////////////////////////////////////////////
#include "UglyObjModel.h"
#include "UglyLog.h"

namespace Ugly
{

/**
 * \brief Constructor.
 */
ObjModel::ObjModel()
{
    mNumVertices = 0;
    mVertexBufferId = 0;
    mTexCoordBufferId = 0;
    mNormalBufferId = 0;
}


/**
 * \brief Destructor.
 */
ObjModel::~ObjModel()
{
    destroy();
}


/**
 * \brief Create model from file.
 *
 * \param   _Filename   File name.
 * \return False if error.
 */
bool ObjModel::create(const std::string &_Filename)
{
    gLog << "Create obj model from: " << _Filename << "\n";

    //Open the file
    std::fstream ifs;
    ifs.open(_Filename.c_str());
    if(ifs.fail())
    {
        gLog << "Error! ObjModel::create: failed to open file\n";
        return false;
    }

    //Temporary storage
    std::vector<glm::vec3> tempVertices;
    std::vector<glm::vec2> tempTexCoords;
    std::vector<glm::vec3> tempNormals;
    std::vector<unsigned int> vertexIndices, texCoordIndices, normalIndices;

    //Get data from file
    char line[256];
    while(ifs.getline(line, 256))
    {
        char lineHeader[128];
        sscanf(line, "%s", lineHeader);

        //Get vertex
        if(strcmp(lineHeader, "v") == 0)
        {
            glm::vec3 vertex;
            sscanf(line, "v %f %f %f\n", &vertex.x, &vertex.y, &vertex.z);
            tempVertices.push_back(vertex);
            gLog << "Vertex: " << vertex << "\n";
        }
        //Get tex coords
        if(strcmp(lineHeader, "vt") == 0)
        {
            glm::vec2 texCoord;
            sscanf(line, "vt %f %f\n", &texCoord.x, &texCoord.y);
            tempTexCoords.push_back(texCoord);
        }
        //Get normal
        else if(strcmp(lineHeader, "vn") == 0)
        {
            glm::vec3 normal;
            sscanf(line, "vn %f %f %f\n", &normal.x, &normal.y, &normal.z);
            tempNormals.push_back(normal);
        }
        //Create faces
        else if(strcmp(lineHeader, "f") == 0)
        {
            unsigned int vertexIndex[3], texCoordIndex[3], normalIndex[3];
            int matches = sscanf(line, "f %d/%d/%d %d/%d/%d %d/%d/%d\n",  &vertexIndex[0], &texCoordIndex[0], &normalIndex[0],
                                                                        &vertexIndex[1], &texCoordIndex[1], &normalIndex[1],
                                                                        &vertexIndex[2], &texCoordIndex[2], &normalIndex[2] );
            if (matches != 9)
            {
                gLog << "Error! ObjModel::create: format is not readable by this parser\n";
                ifs.close();
                return false;
            }

            vertexIndices.push_back(vertexIndex[0]);
            vertexIndices.push_back(vertexIndex[1]);
            vertexIndices.push_back(vertexIndex[2]);
            texCoordIndices.push_back(texCoordIndex[0]);
            texCoordIndices.push_back(texCoordIndex[1]);
            texCoordIndices .push_back(texCoordIndex[2]);
            normalIndices.push_back(normalIndex[0]);
            normalIndices.push_back(normalIndex[1]);
            normalIndices.push_back(normalIndex[2]);
        }
    }
    ifs.close();

    //Create final data
    std::vector<glm::vec3> vertices;
    std::vector<glm::vec2> texCoords;
    std::vector<glm::vec3> normals;
    for( unsigned int i = 0; i < vertexIndices.size(); i++ )
    {
        unsigned int vertexIndex = vertexIndices[i];
        unsigned int texCoordIndex = texCoordIndices[i];
        unsigned int normalIndex = normalIndices[i];

        glm::vec3 vertex = tempVertices[vertexIndex-1];
        glm::vec2 texCoord = tempTexCoords[texCoordIndex-1];
        glm::vec3 normal = tempNormals[normalIndex-1];

        vertices.push_back(vertex);
        texCoords.push_back(texCoord);
        normals.push_back(normal);
    }

    //Save number of vertices
    mNumVertices = vertices.size();

    //Create OpenGL buffer
    glGenBuffers(1, &mVertexBufferId);
    glBindBuffer(GL_ARRAY_BUFFER, mVertexBufferId);
    glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), &vertices[0], GL_STATIC_DRAW);
    glGenBuffers(1, &mTexCoordBufferId);
    glBindBuffer(GL_ARRAY_BUFFER, mTexCoordBufferId);
    glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec2), &texCoords[0], GL_STATIC_DRAW);
    glGenBuffers(1, &mNormalBufferId);
    glBindBuffer(GL_ARRAY_BUFFER, mNormalBufferId);
    glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), &normals[0], GL_STATIC_DRAW);

    return true;
}


/**
 * \brief Destroy model.
 */
void ObjModel::destroy()
{
    mNumVertices = 0;

    //Delete vertex buffer
    if(mVertexBufferId != 0)
    {
        glDeleteBuffers(1, &mVertexBufferId);
        mVertexBufferId = 0;
    }

    //Delete texcoord buffer
    if(mTexCoordBufferId != 0)
    {
        glDeleteBuffers(1, &mTexCoordBufferId);
        mTexCoordBufferId = 0;
    }

    //Delete normal buffer
    if(mNormalBufferId != 0)
    {
        glDeleteBuffers(1, &mNormalBufferId);
        mNormalBufferId = 0;
    }
}


/**
 * \brief Get vertex buffer id.
 *
 * \return Vertex buffer id.
 */
GLuint ObjModel::getVertexBufferId() const
{
    return mVertexBufferId;
}


/**
 * \brief Get texcoord buffer id.
 *
 * \return Texcoord buffer id.
 */
GLuint ObjModel::getTexCoordBufferId() const
{
    return mTexCoordBufferId;
}


/**
 * \brief Get normal buffer id.
 *
 * \return Normal buffer id.
 */
GLuint ObjModel::getNormalBufferId() const
{
    return mNormalBufferId;
}


/**
 * \brief Get the number of vertices.
 *
 * \return Number of vertices.
 */
GLsizei ObjModel::getNumVertices() const
{
    return mNumVertices;
}

}//namespace Ugly
